package com.config.client.server;

import com.alibaba.fastjson.JSONObject;
import com.config.client.cache.ConfigCache;
import com.config.client.config.HbzBeanDefinitionRegistry;
import com.config.client.config.RefreshScope;
import com.registry.client.config.RegistryCentreConfig;
import com.registry.client.config.ServerNameConfig;
import com.utils.HttpClientUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.stereotype.Service;

import java.util.LinkedHashMap;

@Service
public class ConfigServer implements EnvironmentAware , ApplicationListener<ContextRefreshedEvent> {

    private StandardEnvironment environment;
    private HbzBeanDefinitionRegistry hbzBeanDefinitionRegistry;
    private BeanDefinitionRegistry beanDefinitionRegistry;
    @Autowired
    private ConfigurableApplicationContext applicationContext;
    /**
     * 服务名
     */
    @Autowired
    private ServerNameConfig serverNameConfig;
    /**
     * 配置中心服务端地址
     */
    @Autowired
    private RegistryCentreConfig registryCentreConfig;

    @Autowired
    private RefreshScope refreshScope;

    /**
     * 启动长轮询
     */
    public void longPolling() {

        // 获取配置中心服务端地址
        String serverIp = registryCentreConfig.getServerIp();

        // 获取当前服务在配置中心的配置地址
        ConfigCache configCache = ConfigCache.getInstance();
        Integer configId = configCache.getConfigId();

        String httpUrl = "http://" + serverIp + ":8848" + "/check_md5?id=" + configId;
        // 发送http请求长连接
        JSONObject jsonObject = HttpClientUtils.httpGet(httpUrl);

        if(jsonObject != null) {


            System.out.println("有数据变更");
            // 更新缓存中的值
            configCache.setMd5(jsonObject.get("md5") != null ? jsonObject.get("md5").toString() : null);
            configCache.setValue(jsonObject.get("value") != null ? jsonObject.get("value").toString() : null);
            Object configValue = jsonObject.get("configValue");
            LinkedHashMap<String, Object> linkedHashMap = JSONObject.parseObject(JSONObject.toJSONString(configValue), LinkedHashMap.class);

            MutablePropertySources propertySources  = this.applicationContext.getEnvironment().getPropertySources();
            propertySources.forEach(propertySource -> {
                if (propertySource instanceof OriginTrackedMapPropertySource) {
                    OriginTrackedMapPropertySource mapPropertySource
                            = (OriginTrackedMapPropertySource) propertySource;
                    linkedHashMap.forEach((k,v)->{
                        // 更新系统变量
                        System.setProperty(k, v != null ? v.toString() : null);
                        // 更新环境变量
                        mapPropertySource.getSource().put(k,v);
                    });
                    this.refreshBean();

                }

            });

        }else{
            System.out.println("无变更");
        }

        // 再次长轮询
        this.longPolling();


    }

    /**
     * start - end 是参考https://blog.csdn.net/cgj296645438/article/details/107160018/
     * 结合其他的代码才使@Value真正的动态更新
     */
    private void refreshBean() {
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(beanDefinitionName);
            if ("refresh".equals(beanDefinition.getScope())) {
                // start
//                Class<?> aClass = applicationContext.getBean(beanDefinitionName).getClass();
//                beanDefinitionRegistry.removeBeanDefinition(beanDefinitionName);
//                BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(aClass);
//                BeanDefinition newBeanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
//                beanDefinitionRegistry.registerBeanDefinition(beanDefinitionName,newBeanDefinition);
//                newBeanDefinition.setScope("refresh");
                // end
                applicationContext.getBeanFactory().destroyBean(beanDefinitionName);
                refreshScope.remove(beanDefinitionName);
                Object bean = applicationContext.getBean(beanDefinitionName);

                refreshScope.get(beanDefinitionName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        return bean;
                    }
                });


            }
        }
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = (StandardEnvironment) environment;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        hbzBeanDefinitionRegistry = (HbzBeanDefinitionRegistry)
                applicationContext.getBeanFactory().getBean("hbzBeanDefinitionRegistry");
        beanDefinitionRegistry = hbzBeanDefinitionRegistry.getBeanDefinitionRegistry();
    }
}
